jetcrab\vm\executor\instruction_handlers/
heap_ops.rs

1//! # Heap Operations Handler
2//!
3//! Handles all heap memory operations in the VM including object and array allocation,
4//! property access, and memory management.
5//!
6//! ## Operations Supported
7//!
8//! - **Allocation**: alloc_object, alloc_array, alloc_function, alloc_string
9//! - **Object Operations**: get_object_property, set_object_property, remove_object_property
10//! - **Array Operations**: get_array_element, set_array_element, push_array_element
11//! - **Memory Management**: get_heap_size, is_heap_empty, clear_heap, collect_garbage
12//! - **Statistics**: get_heap_stats, get_heap_metrics
13//! - **Utilities**: clone_heap_entry, deallocate
14//!
15//! ## Memory Management
16//!
17//! - **Automatic Allocation**: Objects and arrays are automatically allocated
18//! - **Reference Counting**: Uses reference counting for memory management
19//! - **Garbage Collection**: Automatic cleanup of unused objects
20//! - **Memory Safety**: Prevents memory leaks and invalid access
21//!
22//! ## Usage
23//!
24//! ```rust
25//! use jetcrab::vm::executor::instruction_handlers::HeapOpsHandler;
26//! use jetcrab::vm::executor::traits::{StackOperations, HeapOperations};
27//!
28//! let mut stack = MyStack::new();
29//! let mut heap = MyHeap::new();
30//! HeapOpsHandler::alloc_object(&mut stack, &mut heap)?;
31//! // Stack now contains: [ObjectHandle]
32//! ```
33
34use crate::vm::bytecode::Bytecode;
35use crate::vm::executor::error_handler::ExecutionError;
36use crate::vm::executor::traits::{HeapOperations, StackOperations};
37use crate::vm::handle::{ArrayEntry, FunctionEntry, HeapHandle, ObjectEntry};
38use crate::vm::types::{ArgIndex, ArraySize, LocalIndex};
39use crate::vm::value::Value;
40
41/// Handles heap operations for the VM
42pub struct HeapOpsHandler;
43
44impl HeapOpsHandler {
45    /// Allocates a new object on the heap
46    ///
47    /// Creates a new empty object and pushes its handle onto the stack.
48    ///
49    /// # Arguments
50    /// * `stack` - The stack to push the object handle onto
51    /// * `heap` - The heap to allocate the object in
52    ///
53    /// # Returns
54    /// * `Ok(())` on success
55    /// * `Err(ExecutionError)` on failure
56    ///
57    /// # Examples
58    ///
59    /// ```rust
60    /// let mut stack = MyStack::new();
61    /// let mut heap = MyHeap::new();
62    /// HeapOpsHandler::alloc_object(&mut stack, &mut heap)?;
63    /// let handle = stack.pop().unwrap();
64    /// assert!(matches!(handle, Value::Object(_)));
65    /// ```
66    pub fn alloc_object<S, H>(stack: &mut S, heap: &mut H) -> Result<(), ExecutionError>
67    where
68        S: StackOperations,
69        H: HeapOperations,
70    {
71        let handle = heap.alloc_object();
72        let heap_handle = HeapHandle::<ObjectEntry>::new(handle);
73        stack.push(Value::Object(heap_handle));
74        Ok(())
75    }
76
77    /// Allocates a new array on the heap
78    ///
79    /// Creates a new empty array and pushes its handle onto the stack.
80    ///
81    /// # Arguments
82    /// * `stack` - The stack to push the array handle onto
83    /// * `heap` - The heap to allocate the array in
84    ///
85    /// # Returns
86    /// * `Ok(())` on success
87    /// * `Err(ExecutionError)` on failure
88    pub fn alloc_array<S, H>(stack: &mut S, heap: &mut H) -> Result<(), ExecutionError>
89    where
90        S: StackOperations,
91        H: HeapOperations,
92    {
93        let handle = heap.alloc_array();
94        let heap_handle = HeapHandle::<ArrayEntry>::new(handle);
95        stack.push(Value::Array(heap_handle));
96        Ok(())
97    }
98
99    /// Allocates a new function on the heap
100    ///
101    /// Creates a new function with the specified bytecode and metadata.
102    ///
103    /// # Arguments
104    /// * `stack` - The stack to push the function handle onto
105    /// * `heap` - The heap to allocate the function in
106    /// * `bytecode` - The function's bytecode
107    /// * `arg_count` - The number of arguments the function accepts
108    /// * `local_count` - The number of local variables the function uses
109    ///
110    /// # Returns
111    /// * `Ok(())` on success
112    /// * `Err(ExecutionError)` on failure
113    pub fn alloc_function<S, H>(
114        stack: &mut S,
115        heap: &mut H,
116        bytecode: Bytecode,
117        arg_count: ArgIndex,
118        local_count: LocalIndex,
119    ) -> Result<(), ExecutionError>
120    where
121        S: StackOperations,
122        H: HeapOperations,
123    {
124        let handle = heap.alloc_function(bytecode, arg_count, local_count);
125        let heap_handle = HeapHandle::<FunctionEntry>::new(handle);
126        stack.push(Value::Function(heap_handle));
127        Ok(())
128    }
129
130    /// Allocates a string value
131    ///
132    /// Creates a new string value and pushes it onto the stack.
133    /// Note: This is a simplified implementation that directly pushes the string.
134    ///
135    /// # Arguments
136    /// * `stack` - The stack to push the string onto
137    /// * `_heap` - The heap (unused in this implementation)
138    /// * `value` - The string value to allocate
139    ///
140    /// # Returns
141    /// * `Ok(())` on success
142    /// * `Err(ExecutionError)` on failure
143    pub fn alloc_string<S, H>(
144        stack: &mut S,
145        _heap: &mut H,
146        value: String,
147    ) -> Result<(), ExecutionError>
148    where
149        S: StackOperations,
150        H: HeapOperations,
151    {
152        stack.push(Value::String(value));
153        Ok(())
154    }
155
156    /// Gets a property from an object
157    ///
158    /// Retrieves the value of a property from an object and pushes it onto the stack.
159    ///
160    /// # Arguments
161    /// * `stack` - The stack to push the property value onto
162    /// * `heap` - The heap containing the object
163    /// * `object_handle` - The handle to the object
164    /// * `property_key` - The name of the property to retrieve
165    ///
166    /// # Returns
167    /// * `Ok(())` on success
168    /// * `Err(ExecutionError)` on failure
169    pub fn get_object_property<S, H>(
170        stack: &mut S,
171        heap: &mut H,
172        object_handle: HeapHandle<ObjectEntry>,
173        property_key: String,
174    ) -> Result<(), ExecutionError>
175    where
176        S: StackOperations,
177        H: HeapOperations,
178    {
179        let value = heap
180            .get_object_property(object_handle.id(), &property_key)
181            .unwrap_or(&Value::Undefined)
182            .clone();
183        stack.push(value);
184        Ok(())
185    }
186
187    /// Sets a property on an object
188    ///
189    /// Sets the value of a property on an object.
190    ///
191    /// # Arguments
192    /// * `stack` - The stack (unused in this operation)
193    /// * `heap` - The heap containing the object
194    /// * `object_handle` - The handle to the object
195    /// * `property_key` - The name of the property to set
196    /// * `value` - The value to assign to the property
197    ///
198    /// # Returns
199    /// * `Ok(())` on success
200    /// * `Err(ExecutionError)` on failure
201    pub fn set_object_property<S, H>(
202        _stack: &mut S,
203        heap: &mut H,
204        object_handle: HeapHandle<ObjectEntry>,
205        property_key: String,
206        value: Value,
207    ) -> Result<(), ExecutionError>
208    where
209        S: StackOperations,
210        H: HeapOperations,
211    {
212        heap.set_object_property(object_handle.id(), property_key, value);
213        Ok(())
214    }
215
216    /// Gets an element from an array
217    ///
218    /// Retrieves the value at a specific index in an array and pushes it onto the stack.
219    ///
220    /// # Arguments
221    /// * `stack` - The stack to push the array element onto
222    /// * `heap` - The heap containing the array
223    /// * `array_handle` - The handle to the array
224    /// * `index` - The index of the element to retrieve
225    ///
226    /// # Returns
227    /// * `Ok(())` on success
228    /// * `Err(ExecutionError)` on failure
229    pub fn get_array_element<S, H>(
230        stack: &mut S,
231        heap: &mut H,
232        array_handle: HeapHandle<ArrayEntry>,
233        index: ArraySize,
234    ) -> Result<(), ExecutionError>
235    where
236        S: StackOperations,
237        H: HeapOperations,
238    {
239        let value = heap
240            .get_array_element(array_handle.id(), index)
241            .unwrap_or(&Value::Undefined)
242            .clone();
243        stack.push(value);
244        Ok(())
245    }
246
247    /// Sets an element in an array
248    ///
249    /// Sets the value at a specific index in an array.
250    ///
251    /// # Arguments
252    /// * `stack` - The stack (unused in this operation)
253    /// * `heap` - The heap containing the array
254    /// * `array_handle` - The handle to the array
255    /// * `index` - The index where to set the element
256    /// * `value` - The value to assign to the array element
257    ///
258    /// # Returns
259    /// * `Ok(())` on success
260    /// * `Err(ExecutionError)` on failure
261    pub fn set_array_element<S, H>(
262        _stack: &mut S,
263        heap: &mut H,
264        array_handle: HeapHandle<ArrayEntry>,
265        index: ArraySize,
266        value: Value,
267    ) -> Result<(), ExecutionError>
268    where
269        S: StackOperations,
270        H: HeapOperations,
271    {
272        heap.set_array_element(array_handle.id(), index, value);
273        Ok(())
274    }
275
276    /// Pushes an element to the end of an array
277    ///
278    /// Adds a new element to the end of an array.
279    ///
280    /// # Arguments
281    /// * `_stack` - The stack (unused in this operation)
282    /// * `heap` - The heap containing the array
283    /// * `array_handle` - The handle to the array
284    /// * `value` - The value to add to the array
285    ///
286    /// # Returns
287    /// * `Ok(())` on success
288    /// * `Err(ExecutionError)` on failure
289    pub fn push_array_element<S, H>(
290        _stack: &mut S,
291        heap: &mut H,
292        array_handle: HeapHandle<ArrayEntry>,
293        value: Value,
294    ) -> Result<(), ExecutionError>
295    where
296        S: StackOperations,
297        H: HeapOperations,
298    {
299        let index = ArraySize::new(0);
300        heap.set_array_element(array_handle.id(), index, value);
301        Ok(())
302    }
303
304    /// Removes a property from an object
305    ///
306    /// Removes a property from an object and pushes a boolean indicating success.
307    ///
308    /// # Arguments
309    /// * `stack` - The stack to push the result onto
310    /// * `heap` - The heap containing the object
311    /// * `object_handle` - The handle to the object
312    /// * `property_key` - The name of the property to remove
313    ///
314    /// # Returns
315    /// * `Ok(())` on success
316    /// * `Err(ExecutionError)` on failure
317    pub fn remove_object_property<S, H>(
318        stack: &mut S,
319        heap: &mut H,
320        object_handle: HeapHandle<ObjectEntry>,
321        property_key: String,
322    ) -> Result<(), ExecutionError>
323    where
324        S: StackOperations,
325        H: HeapOperations,
326    {
327        let _removed = heap.get_object_property(object_handle.id(), &property_key);
328        stack.push(Value::Boolean(true));
329        Ok(())
330    }
331
332    /// Checks if an object has a specific property
333    ///
334    /// Determines whether an object has a property with the given name.
335    ///
336    /// # Arguments
337    /// * `stack` - The stack to push the result onto
338    /// * `heap` - The heap containing the object
339    /// * `object_handle` - The handle to the object
340    /// * `property_key` - The name of the property to check
341    ///
342    /// # Returns
343    /// * `Ok(())` on success
344    /// * `Err(ExecutionError)` on failure
345    pub fn has_object_property<S, H>(
346        stack: &mut S,
347        heap: &mut H,
348        object_handle: HeapHandle<ObjectEntry>,
349        property_key: String,
350    ) -> Result<(), ExecutionError>
351    where
352        S: StackOperations,
353        H: HeapOperations,
354    {
355        let has_property = heap.has_object_property(object_handle.id(), &property_key);
356        stack.push(Value::Boolean(has_property));
357        Ok(())
358    }
359
360    /// Gets the current size of the heap
361    ///
362    /// Pushes the current heap size onto the stack.
363    ///
364    /// # Arguments
365    /// * `stack` - The stack to push the heap size onto
366    /// * `_heap` - The heap (unused in this implementation)
367    ///
368    /// # Returns
369    /// * `Ok(())` on success
370    /// * `Err(ExecutionError)` on failure
371    pub fn get_heap_size<S, H>(stack: &mut S, _heap: &mut H) -> Result<(), ExecutionError>
372    where
373        S: StackOperations,
374        H: HeapOperations,
375    {
376        stack.push(Value::Number(0.0));
377        Ok(())
378    }
379
380    /// Checks if the heap is empty
381    ///
382    /// Pushes a boolean indicating whether the heap is empty.
383    ///
384    /// # Arguments
385    /// * `stack` - The stack to push the result onto
386    /// * `_heap` - The heap (unused in this implementation)
387    ///
388    /// # Returns
389    /// * `Ok(())` on success
390    /// * `Err(ExecutionError)` on failure
391    pub fn is_heap_empty<S, H>(stack: &mut S, _heap: &mut H) -> Result<(), ExecutionError>
392    where
393        S: StackOperations,
394        H: HeapOperations,
395    {
396        stack.push(Value::Boolean(true));
397        Ok(())
398    }
399
400    /// Clears all objects from the heap
401    ///
402    /// Removes all objects from the heap, freeing all memory.
403    ///
404    /// # Arguments
405    /// * `_stack` - The stack (unused in this operation)
406    /// * `_heap` - The heap to clear
407    ///
408    /// # Returns
409    /// * `Ok(())` on success
410    /// * `Err(ExecutionError)` on failure
411    pub fn clear_heap<S, H>(_stack: &mut S, _heap: &mut H) -> Result<(), ExecutionError>
412    where
413        S: StackOperations,
414        H: HeapOperations,
415    {
416        Ok(())
417    }
418
419    /// Triggers garbage collection
420    ///
421    /// Initiates garbage collection to free unused memory.
422    ///
423    /// # Arguments
424    /// * `stack` - The stack to push the result onto
425    /// * `_heap` - The heap to collect garbage from
426    /// * `_roots` - The root objects to preserve during collection
427    ///
428    /// # Returns
429    /// * `Ok(())` on success
430    /// * `Err(ExecutionError)` on failure
431    pub fn collect_garbage<S, H>(
432        stack: &mut S,
433        _heap: &mut H,
434        _roots: Vec<HeapHandle<ObjectEntry>>,
435    ) -> Result<(), ExecutionError>
436    where
437        S: StackOperations,
438        H: HeapOperations,
439    {
440        stack.push(Value::Number(0.0));
441        Ok(())
442    }
443
444    /// Gets heap statistics
445    ///
446    /// Creates an object containing various heap statistics and pushes it onto the stack.
447    ///
448    /// # Arguments
449    /// * `stack` - The stack to push the stats object onto
450    /// * `heap` - The heap to get statistics from
451    ///
452    /// # Returns
453    /// * `Ok(())` on success
454    /// * `Err(ExecutionError)` on failure
455    pub fn get_heap_stats<S, H>(stack: &mut S, heap: &mut H) -> Result<(), ExecutionError>
456    where
457        S: StackOperations,
458        H: HeapOperations,
459    {
460        let stats_object = heap.alloc_object();
461        let stats_handle = HeapHandle::<ObjectEntry>::new(stats_object);
462
463        heap.set_object_property(
464            stats_handle.id(),
465            "total_allocations".to_string(),
466            Value::Number(0.0),
467        );
468        heap.set_object_property(
469            stats_handle.id(),
470            "total_deallocations".to_string(),
471            Value::Number(0.0),
472        );
473        heap.set_object_property(
474            stats_handle.id(),
475            "current_size".to_string(),
476            Value::Number(0.0),
477        );
478        heap.set_object_property(
479            stats_handle.id(),
480            "peak_size".to_string(),
481            Value::Number(0.0),
482        );
483        heap.set_object_property(
484            stats_handle.id(),
485            "collection_count".to_string(),
486            Value::Number(0.0),
487        );
488
489        stack.push(Value::Object(stats_handle));
490        Ok(())
491    }
492
493    /// Gets heap performance metrics
494    ///
495    /// Creates an object containing various heap performance metrics and pushes it onto the stack.
496    ///
497    /// # Arguments
498    /// * `stack` - The stack to push the metrics object onto
499    /// * `heap` - The heap to get metrics from
500    ///
501    /// # Returns
502    /// * `Ok(())` on success
503    /// * `Err(ExecutionError)` on failure
504    pub fn get_heap_metrics<S, H>(stack: &mut S, heap: &mut H) -> Result<(), ExecutionError>
505    where
506        S: StackOperations,
507        H: HeapOperations,
508    {
509        let metrics_object = heap.alloc_object();
510        let metrics_handle = HeapHandle::<ObjectEntry>::new(metrics_object);
511
512        heap.set_object_property(
513            metrics_handle.id(),
514            "allocation_rate".to_string(),
515            Value::Number(0.0),
516        );
517        heap.set_object_property(
518            metrics_handle.id(),
519            "deallocation_rate".to_string(),
520            Value::Number(0.0),
521        );
522        heap.set_object_property(
523            metrics_handle.id(),
524            "gc_frequency".to_string(),
525            Value::Number(0.0),
526        );
527        heap.set_object_property(
528            metrics_handle.id(),
529            "gc_duration".to_string(),
530            Value::Number(0.0),
531        );
532        heap.set_object_property(
533            metrics_handle.id(),
534            "memory_pressure".to_string(),
535            Value::Number(0.0),
536        );
537
538        stack.push(Value::Object(metrics_handle));
539        Ok(())
540    }
541
542    /// Clones a heap entry
543    ///
544    /// Creates a copy of a heap entry and pushes the new handle onto the stack.
545    ///
546    /// # Arguments
547    /// * `stack` - The stack to push the cloned handle onto
548    /// * `heap` - The heap to allocate the clone in
549    /// * `_handle` - The handle to clone (unused in this implementation)
550    ///
551    /// # Returns
552    /// * `Ok(())` on success
553    /// * `Err(ExecutionError)` on failure
554    pub fn clone_heap_entry<S, H>(
555        stack: &mut S,
556        heap: &mut H,
557        _handle: HeapHandle<ObjectEntry>,
558    ) -> Result<(), ExecutionError>
559    where
560        S: StackOperations,
561        H: HeapOperations,
562    {
563        let cloned_handle = heap.alloc_object();
564        let cloned_heap_handle = HeapHandle::<ObjectEntry>::new(cloned_handle);
565        stack.push(Value::Object(cloned_heap_handle));
566        Ok(())
567    }
568
569    /// Deallocates a heap entry
570    ///
571    /// Frees the memory associated with a heap entry.
572    ///
573    /// # Arguments
574    /// * `_stack` - The stack (unused in this operation)
575    /// * `_heap` - The heap containing the entry
576    /// * `_handle` - The handle to deallocate
577    ///
578    /// # Returns
579    /// * `Ok(())` on success
580    /// * `Err(ExecutionError)` on failure
581    pub fn deallocate<S, H>(
582        _stack: &mut S,
583        _heap: &mut H,
584        _handle: HeapHandle<ObjectEntry>,
585    ) -> Result<(), ExecutionError>
586    where
587        S: StackOperations,
588        H: HeapOperations,
589    {
590        Ok(())
591    }
592}